
Imports System.IO
Imports bithelper

Public Class bitreader

    Public Const bufflen As Int32 = 4096
    Public Const retrytimes As Int32 = 32

    Protected Friend input As Stream
    Protected Friend offset As Int32
    Protected Friend bs As bytes = New bytes()
    Protected Friend inoffset As Byte

    Protected Friend Sub reset()
        offset = bufflen
        inoffset = 0
        bs.size() = bufflen
    End Sub

    Public Function setstream(ByVal s As Stream) As Boolean
        If s.CanRead Then
            input = s
            reset()
            Return True
        Else
            Return False
        End If
    End Function

    Public Function seeknext(ByVal bit As Boolean) As Int64
        assert(Not input Is Nothing, "must set a valid stream before seek")

        Dim rtn As Int64 = 0
        Dim readbit As Boolean = False
        If Not read(readbit) Then
            Return npos
        End If
        While readbit <> bit
            rtn += 1

            If Not read(readbit) Then
                Return npos
            End If
        End While

        Return rtn
    End Function

    Public Function read(ByRef bit As Boolean) As Boolean
        assert(Not input Is Nothing, "must set a valid stream before seek")

        If inoffset = 8 Then
            offset += 1
            inoffset = 0
        End If

        If offset = bs.size() Then
            Dim readcount As Int32
            readcount = readstream(input, +bs, , bufflen, retrytimes)
            If readcount = npos OrElse readcount = 0 Then
                Return False
            Else
                offset = 0
                'inoffset = 0 inoffset always 0 when offset changed
                bs.size = readcount
            End If
        End If

        bit = getbit(bs(offset), inoffset)
        inoffset += 1

        Return True
    End Function

End Class

Public Class bitwriter

    Public Const bufflen As Int32 = 4096
    Public Const retrytimes As Int32 = 32

    Private output As Stream
    Private offset As Int32
    Private bs As bytes = New bytes()
    Private inoffset As Byte

    Private Sub reset()
        offset = 0
        inoffset = 0
        bs.size() = bufflen
    End Sub

    Public Function setstream(ByVal s As Stream) As Boolean
        If s.CanWrite Then
            output = s
            reset()
            Return True
        Else
            Return False
        End If
    End Function

    Public Function write(ByVal bit As Boolean) As Boolean
        assert(Not output Is Nothing, "must set a valid stream before seek")

        If inoffset = 8 Then
            offset += 1
            inoffset = 0
        End If

        If offset = bufflen Then
            If writestream(output, +bs, 0, bufflen, retrytimes) Then
                offset = 0
                'inoffset = 0 inoffset always 0 when offset changed
            Else
                Return False
            End If
        End If

        setbit(bs(offset), inoffset, bit)
        inoffset += 1

        Return True
    End Function

    Public Function flush() As Boolean
        If offset > 0 Then
            Dim i As Int32
            For i = 0 To retrytimes - 1
                Try
                    output.Write(+bs, 0, offset)
                    Exit For
                Catch
                End Try
            Next

            If i = retrytimes Then
                Return False
            Else
                bs(0) = bs(offset)
                offset = 0
            End If
        End If

        output.Flush()
        Return True
    End Function

    Public Function close() As Boolean
        If Not flush() Then
            Return False
        Else
            Dim i As Int32
            For i = 0 To retrytimes - 1
                Try
                    output.Write(+bs, 0, 1)
                    Exit For
                Catch
                End Try
            Next

            If i = retrytimes Then
                Return False
            Else
                offset = 0
                inoffset = 0
            End If
        End If

        output.Flush()
        Return True
    End Function

End Class
